<template>
  <mxp-popup
    ref="popupRef"
    model="bottom"
    :animationEndShowContent="false"
    :background="options.background || defThemeColor.fill"
    :offsetBottom="options.offset"
    :toolbarOptions="options.toolbarOptions"
    :zIndex="options.zIndex"
    :showToolbar="options.showToolbar"
    :maskBackground="options.maskBackground"
    :maskClose="options.maskClose"
    @hide="data.showPicker = false"
    @animationEnd="onAnimationEnd"
  >
    <view style="width: 750rpx">
      <slot name="header"></slot>
      <view v-if="data.showPicker" class="picker-view-box-vessel">
        <picker-view
          id="firstList"
          class="picker-view"
          :indicatorStyle="`height: ${options.itemHeight};line-height: ${options.itemHeight};`"
          :value="data.defaultDateIndex"
          :style="{ height: `calc(${options.itemHeight} * ${options.showItemCount})`, width: '750rpx' }"
          @change="bindChange"
          @pickstart="pickstart"
          @pickend="pickend"
        >
          <picker-view-column>
            <view v-for="(item, index) in data.yearArr" :key="index" class="picker-item" :style="{ height: options.itemHeight }">
              <text
                :style="{
                  fontSize: index === data.defaultDateIndex[0] ? options.activeFontSize : options.fontSize,
                  color: index === data.defaultDateIndex[0] ? options.activeColor || defThemeColor.text : options.color || defThemeColor.text2,
                }"
              >
                {{ item }}年
              </text>
            </view>
          </picker-view-column>
          <picker-view-column>
            <view v-for="(item, index) in data.monthArr" :key="index" class="picker-item" :style="{ height: options.itemHeight }">
              <text
                :style="{
                  fontSize: index === data.defaultDateIndex[1] ? options.activeFontSize : options.fontSize,
                  color: index === data.defaultDateIndex[1] ? options.activeColor || defThemeColor.text : options.color || defThemeColor.text2,
                }"
              >
                {{ item }}月
              </text>
            </view>
          </picker-view-column>
          <picker-view-column>
            <view v-for="(item, index) in data.dayArr" :key="index" class="picker-item" :style="{ height: options.itemHeight }">
              <text
                :style="{
                  fontSize: index === data.defaultDateIndex[2] ? options.activeFontSize : options.fontSize,
                  color: index === data.defaultDateIndex[2] ? options.activeColor || defThemeColor.text : options.color || defThemeColor.text2,
                }"
              >
                {{ item }}日
              </text>
            </view>
          </picker-view-column>
          <picker-view-column>
            <view v-for="(item, index) in data.hourArr" :key="index" class="picker-item" :style="{ height: options.itemHeight }">
              <text
                :style="{
                  fontSize: index === data.defaultDateIndex[3] ? options.activeFontSize : options.fontSize,
                  color: index === data.defaultDateIndex[3] ? options.activeColor || defThemeColor.text : options.color || defThemeColor.text2,
                }"
              >
                {{ item }}时
              </text>
            </view>
          </picker-view-column>
          <picker-view-column>
            <view v-for="(item, index) in data.minuteArr" :key="index" class="picker-item" :style="{ height: options.itemHeight }">
              <text
                :style="{
                  fontSize: index === data.defaultDateIndex[4] ? options.activeFontSize : options.fontSize,
                  color: index === data.defaultDateIndex[4] ? options.activeColor || defThemeColor.text : options.color || defThemeColor.text2,
                }"
              >
                {{ item }}分
              </text>
            </view>
          </picker-view-column>
          <picker-view-column v-if="options.showSecond">
            <view v-for="(item, index) in data.secondArr" :key="index" class="picker-item" :style="{ height: options.itemHeight }">
              <text
                :style="{
                  fontSize: index === data.defaultDateIndex[5] ? options.activeFontSize : options.fontSize,
                  color: index === data.defaultDateIndex[5] ? options.activeColor || defThemeColor.text : options.color || defThemeColor.text2,
                }"
              >
                {{ item }}秒
              </text>
            </view>
          </picker-view-column>
        </picker-view>
      </view>
      <slot name="bottom"></slot>
    </view>
  </mxp-popup>
</template>

<script lang="ts" setup>
  import MxpPopup from '../mxp-popup/mxp-popup.vue';
  import type { $ToolbarProps } from '../mxp-toolbar/mxp-toolbar.vue';
  import { reactive, ref } from 'vue';
  import type { $AllColorStr, $SizeUnitStr } from '../mxp-tool/types/interface';
  import { removeObjectEmpty } from '../mxp-tool/utils/obj';
  import type { PickerViewOnChangeEvent } from 'mxp-uni-types';
  import { defThemeColor } from '../mxp-tool/theme/theme';

  export type $PickerDateTimeProps = {
    /** 背景颜色 */
    background?: $AllColorStr; // eslint-disable-line vue/no-unused-properties
    /** 文本的颜色 */
    color?: $AllColorStr; // eslint-disable-line vue/no-unused-properties
    /** 选中的文本颜色 */
    activeColor?: $AllColorStr; // eslint-disable-line vue/no-unused-properties
    /** 默认文字的大小 */
    fontSize?: $SizeUnitStr; // eslint-disable-line vue/no-unused-properties
    /** 选中的默认文字的大小 */
    activeFontSize?: $SizeUnitStr; // eslint-disable-line vue/no-unused-properties
    /** 偏移量 （如自定义底部的tabbar就需要一个偏移量） */
    offset?: $SizeUnitStr; // eslint-disable-line vue/no-unused-properties
    /** 是否显示秒 */
    showSecond?: boolean; // eslint-disable-line vue/no-unused-properties
    /** 最小日期 */
    minDate?: Date | number | string; // eslint-disable-line vue/no-unused-properties
    /** 最大日期 */
    maxDate?: Date | number | string; // eslint-disable-line vue/no-unused-properties
    /** 默认日期 */
    defaultDate?: Date | number | string; // eslint-disable-line vue/no-unused-properties
    /** 是否留出安全区域 */
    safeSrea?: boolean; // eslint-disable-line vue/no-unused-properties
    /** zIndex */
    zIndex?: number; // eslint-disable-line vue/no-unused-properties
    /** 标题 */
    title?: string; // eslint-disable-line vue/no-unused-properties
    /** 是否显示顶部工具栏 */
    showToolbar?: boolean; // eslint-disable-line vue/no-unused-properties
    /** 顶部工具栏配置项 */
    toolbarOptions?: $ToolbarProps; // eslint-disable-line vue/no-unused-properties
    /** 每列显示多少排 */
    showItemCount?: number; // eslint-disable-line vue/no-unused-properties
    /** 单个选项的高度 */
    itemHeight?: $SizeUnitStr; // eslint-disable-line vue/no-unused-properties
    /** 遮罩的背景色 */
    maskBackground?: $AllColorStr; // eslint-disable-line vue/no-unused-properties
    /** 是否可以点击遮罩关闭 */
    maskClose?: boolean; // eslint-disable-line vue/no-unused-properties
  };
  export type $CallBack = {
    confirm?: (data: string) => void;
    change?: (data: { selected: string; levelIndex: number; index: number; value: string }) => void;
  };
  export type $Options = $CallBack;

  defineOptions({
    name: 'mxp-picker-date-time',
  });
  const props = withDefaults(defineProps<$PickerDateTimeProps>(), {
    background: undefined as $AllColorStr | undefined,
    offset: '0px',
    showSecond: false,
    minDate: () => new Date(new Date().getFullYear() - 100, 0, 1, 0, 0, 0),
    maxDate: () => new Date(new Date().getFullYear() + 100, 11, 31, 23, 59, 59),
    defaultDate: () => new Date(),
    safeSrea: true,
    zIndex: 888,
    title: '请选择日期',
    toolbarOptions: () => ({}),
    showToolbar: true,
    color: undefined as $AllColorStr | undefined,
    activeColor: undefined as $AllColorStr | undefined,
    fontSize: '14px',
    activeFontSize: '16px',
    showItemCount: 7,
    itemHeight: '44px',
    maskBackground: undefined as $AllColorStr | undefined,
    maskClose: undefined as boolean | undefined,
  });
  defineSlots<{
    /** 自定义顶部内容 */
    header: () => void;
    /** 自定义底部内容 */
    bottom: () => void;
  }>();

  const global_date = new Date();
  const global_date_start = new Date(new Date().getFullYear() - 100, 0, 1, 0, 0, 0);
  const global_date_end = new Date(new Date().getFullYear() + 100, 11, 31, 23, 59, 59);

  const popupRef = ref<InstanceType<typeof MxpPopup> | null>(null);

  const options = ref<$CallBack & Required<$PickerDateTimeProps>>(JSON.parse(JSON.stringify(props)));
  type $Data = {
    showPicker: boolean;
    isSubmit: boolean;
    yearArr: string[];
    monthArr: string[];
    dayArr: string[];
    hourArr: string[];
    minuteArr: string[];
    secondArr: string[];
    defaultDateIndex: number[];
    minDateNew: Date;
    maxDateNew: Date;
    defaultDateNew: Date;
  };
  const data: $Data = reactive({
    showPicker: false,
    isSubmit: true,
    minDateNew: global_date_start,
    maxDateNew: global_date_end,
    defaultDateNew: global_date,
    yearArr: [],
    monthArr: [],
    dayArr: [],
    hourArr: [],
    minuteArr: [],
    secondArr: [],
    defaultDateIndex: [0, 0, 0, 0, 0, 0],
  });

  /** 显示 */
  function show(param: $Options = {}) {
    options.value = { ...props, ...removeObjectEmpty(param) };
    options.value.toolbarOptions.title = options.value.title;
    init();
    initData();
    data.showPicker = false;
    popupRef.value?.show({
      confirm() {
        confirm();
      },
    });
    setTimeout(() => {
      data.showPicker = true;
    }, 10);
  }
  /** 监听弹出框动画完成 */
  function onAnimationEnd(type: 'hide-animation' | 'show-animation') {
    if (type === 'show-animation') {
      data.showPicker = true;
    } else {
      data.showPicker = false;
    }
  }

  /**
   * 隐藏
   */
  function hide() {
    popupRef.value?.hide();
  }
  /**
   * 更新日期 (外部调用此方法可更新日期)
   * @param date - 日期字符串或日期字符串数组
   */
  function undate(date: Date | number) {
    initData(date);
  }

  /** 提交 */
  function confirm() {
    if (data.isSubmit) {
      const date = getSubmitDateFun(
        options.value.showSecond,
        data.defaultDateIndex,
        data.yearArr,
        data.monthArr,
        data.dayArr,
        data.hourArr,
        data.minuteArr,
        data.secondArr
      );
      typeof options.value.confirm === 'function' && options.value.confirm(date);
      hide();
    }
  }

  /** 初始化 */
  function init() {
    data.showPicker = false;
    data.yearArr = [];
    data.monthArr = [];
    data.dayArr = [];
    data.hourArr = [];
    data.minuteArr = [];
    data.secondArr = [];
    data.defaultDateIndex = [0, 0, 0, 0, 0, 0];
  }

  /** 初始化数据 */
  function initData(date = options.value.defaultDate) {
    data.minDateNew = getStringDate(options.value.minDate, 'start');
    data.maxDateNew = getStringDate(options.value.maxDate, 'end');
    data.defaultDateNew = getStringDate(date, 'default');

    if (data.minDateNew.getTime() > data.maxDateNew.getTime()) {
      data.minDateNew = data.maxDateNew;
      data.maxDateNew = data.minDateNew;
      console.error(`传入最小日期大于最大日期，建议改正(本组件会自动以交换最大和最小日期)`);
    }
    if (data.minDateNew.getTime() > data.defaultDateNew.getTime()) {
      data.minDateNew = data.defaultDateNew;
      console.error(`传入最小日期小于默认日期，建议改正(本组件会自动以自动把默认日期设为最小日期)`);
    }
    if (data.maxDateNew.getTime() < data.defaultDateNew.getTime()) {
      data.maxDateNew = data.defaultDateNew;
      console.error(`传入最大日期小于默认日期，建议改正(本组件会自动以自动把默认日期设为最大日期)`);
    }

    const defArr = [
      data.defaultDateNew.getFullYear(),
      data.defaultDateNew.getMonth() + 1,
      data.defaultDateNew.getDate(),
      data.defaultDateNew.getHours(),
      data.defaultDateNew.getMinutes(),
      data.defaultDateNew.getSeconds(),
    ];
    data.yearArr = createDateArray(data.minDateNew.getFullYear(), data.maxDateNew.getFullYear());
    data.monthArr = getMonthArr(data.minDateNew, data.maxDateNew, defArr);
    data.dayArr = getDayArr(data.minDateNew, data.maxDateNew, defArr);
    data.hourArr = createDateArray(data.minDateNew.getHours(), data.maxDateNew.getHours());
    data.minuteArr = createDateArray(data.minDateNew.getMinutes(), data.maxDateNew.getMinutes());
    data.secondArr = createDateArray(data.minDateNew.getSeconds(), data.maxDateNew.getSeconds());
    data.defaultDateIndex = getDateIndex(data.defaultDateNew, data.yearArr, data.monthArr, data.dayArr, data.hourArr, data.minuteArr, data.secondArr);
  }

  /** 滚轮改变 */
  function bindChange(e: PickerViewOnChangeEvent) {
    const val = e.detail.value;
    // 得到是那一列的滚轮发生变化的索引值
    const levelIndex = val.findIndex((item, i) => item !== (data.defaultDateIndex[i] || 0));
    const changeIndex = val[levelIndex]!;
    data.defaultDateIndex = val;

    const y = parseInt(data.yearArr[val[0]!]!);
    let m = parseInt(data.monthArr[val[1]!]!);
    let d = parseInt(data.dayArr[val[2]!]!);
    // 年份份改变，重置月和日
    if (levelIndex <= 0) {
      data.monthArr = getMonthArr(data.minDateNew, data.maxDateNew, [y, m, d]);
    }
    // 年份或者月份改变，重置日
    if (levelIndex <= 1) {
      m = parseInt(data.monthArr[val[1]!]!);
      d = parseInt(data.dayArr[val[2]!]!);
      data.dayArr = getDayArr(data.minDateNew, data.maxDateNew, [y, m, d]);
    }
    const date = getSubmitDateFun(
      options.value.showSecond,
      data.defaultDateIndex,
      data.yearArr,
      data.monthArr,
      data.dayArr,
      data.hourArr,
      data.minuteArr,
      data.secondArr
    );
    const keyObj = { 0: 'yearArr', 1: 'monthArr', 2: 'dayArr', 3: 'hourArr', 4: 'minuteArr', 5: 'secondArr' };
    const key = keyObj[levelIndex as keyof typeof keyObj] as 'dayArr' | 'hourArr' | 'minuteArr' | 'monthArr' | 'secondArr' | 'yearArr';
    const res = { selected: date, levelIndex, index: changeIndex, value: data[key][changeIndex]! };
    typeof options.value.change === 'function' && options.value.change(res);
  }

  /** 滚动开始 */
  function pickstart() {
    data.isSubmit = false;
  }
  /** 滚动结束 */
  function pickend() {
    data.isSubmit = true;
  }

  /** 得到月滚轮的数组 */
  function getMonthArr(min: Date, max: Date, defArr: number[]): string[] {
    const minY = min.getFullYear();
    const maxY = max.getFullYear();
    const defY = defArr[0];
    const minM = min.getMonth() + 1;
    const maxM = max.getMonth() + 1;
    let minNumb = 1;
    let maxNumb = 12;
    if (minY === defY) {
      minNumb = minM;
    }
    if (maxY === defY) {
      maxNumb = maxM;
    }
    return createDateArray(minNumb, maxNumb);
  }

  /** 得到日滚轮的数组 */
  function getDayArr(min: Date, max: Date, defArr: number[]): string[] {
    const minY = min.getFullYear();
    const maxY = max.getFullYear();
    const defY = defArr[0]!;
    const minM = min.getMonth() + 1;
    const maxM = max.getMonth() + 1;
    const defM = defArr[1]!;
    const minD = min.getDate();
    const maxD = max.getDate();
    let minNumb = 1;
    if (minY === defY && minM === defM) {
      minNumb = minD;
    }
    let maxNumb = new Date(defY, defM, 0).getDate();
    if (maxY === defY && maxM === defM) {
      maxNumb = maxD;
    }
    return createDateArray(minNumb, maxNumb);
  }

  /** 创建数组 */
  function createDateArray(start: number, end: number) {
    const arr = [];
    if (!isNaN(start) && !isNaN(end) && end - start <= 3000) {
      for (let i = start; i <= end; i++) {
        arr.push(getdoubleDigit(i));
      }
    }
    return arr;
  }

  /** 时间处理（容错）（遇到非法时间则转换成默认时间 */
  function getStringDate(dateStr: Date | number | string | undefined, type: 'default' | 'end' | 'start') {
    let date = dateStr;
    if (!dateStr) {
      date = type === 'start' ? global_date_start : type === 'end' ? global_date_end : global_date;
    }
    if (typeof dateStr === 'string') {
      date = dateStr.substring(0, 19);
      date = date.replace(/-/g, '/'); // 必须把日期'-'转为'/'
    }
    date = new Date(date!);

    if (type === 'end' && date.getHours() === 0 && date.getMinutes() === 0 && date.getSeconds() === 0) {
      date.setHours(23);
      date.setMinutes(59);
      date.setSeconds(59);
    }

    if (isNaN(date.getFullYear()) || isNaN(date.getMonth()) || isNaN(date.getDate())) {
      date = type === 'start' ? global_date_start : type === 'end' ? global_date_end : global_date;
      if (dateStr) {
        console.error(`传入${type === 'start' ? '最小' : type === 'end' ? '最大' : '默认'}时间不合法。`);
      }
    }
    return date;
  }

  /**
   * 得到默认时间的下标
   */
  function getDateIndex(date: Date, yearArr: string[], monthArr: string[], dayArr: string[], hourArr: string[], minuteArr: string[], secondArr: string[]) {
    const year = yearArr.indexOf(getdoubleDigit(date.getFullYear()));
    const month = monthArr.indexOf(getdoubleDigit(date.getMonth() + 1));
    const day = dayArr.indexOf(getdoubleDigit(date.getDate()));

    const hour = hourArr.indexOf(getdoubleDigit(date.getHours()));
    const minute = minuteArr.indexOf(getdoubleDigit(date.getMinutes()));
    const second = secondArr.indexOf(getdoubleDigit(date.getSeconds()));

    return [year > 0 ? year : 0, month > 0 ? month : 0, day > 0 ? day : 0, hour > 0 ? hour : 0, minute > 0 ? minute : 0, second > 0 ? second : 0];
  }

  /**
   * 得到最终需要返回的时间
   */
  function getSubmitDateFun(
    showSecond: boolean,
    indexArr: number[],
    yearArr: string[],
    monthArr: string[],
    dayArr: string[],
    hourArr: string[],
    minuteArr: string[],
    secondArr: string[]
  ) {
    const year = yearArr[indexArr[0]!];
    const month = monthArr[indexArr[1]!];
    const day = dayArr[indexArr[2]!];
    const hour = hourArr[indexArr[3]!];
    const minute = indexArr.length > 3 ? minuteArr[indexArr[4]!] : '00';
    const second = indexArr.length > 4 ? secondArr[indexArr[5]!] : '00';

    const selectDate = `${year}-${month}-${day} ${hour}:${minute}${showSecond ? `:${second}` : ''}`;
    return selectDate;
  }

  /** 得到两位数 */
  function getdoubleDigit(numb: number) {
    return numb < 10 ? `0${numb}` : `${numb}`;
  }

  defineExpose({
    /** 显示 */
    show,
    /** 隐藏 */
    hide,
    /** 更新日期 */
    undate,
    /** 提交 */
    confirm,
  });
</script>

<style lang="scss" scoped>
  .picker-view-box-vessel {
    display: flex;
    flex-flow: column nowrap;
    align-items: center;
    justify-content: space-between;
    width: 750rpx;

    .picker-view {
      width: 750rpx;
      height: 200px;

      .picker-item {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        height: 40px;
        font-size: 13px;
        line-height: 40px;
        text-align: center;
      }
    }
  }
</style>
